package net.entelijan.cobean.worker;

import javax.swing.JOptionPane;

import net.entelijan.cobean.worker.api.ITaskCompanion;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Executes a task. A task can be any public method in any class with no
 * parameter or an ITaskCompanion as parameter.
 * 
 * @param <R>
 *            The return value of the task. Use Void if the task has no return
 *            value.
 */
public abstract class CobeanWorker<R> {

	private static Log log = LogFactory.getLog(CobeanWorker.class);

	public CobeanWorker() {
		super();
	}

	protected abstract R doInBackground(final ITaskCompanion companion)
			throws Exception;

	public void execute() {
		execute(new IInternalTaskCompanion<R>() {

			@Override
			public void finished(R returnValue) {
				log.info("[finished] Default companion. Returnvalue: '"
						+ returnValue + "'");
			}

			@Override
			public void setException(Exception e) {
				log
						.warn(
								"[setException] Default companion. An exception occured during task execution. "
										+ "You must define an ITaskCompanion for your task in order to handle that exception. "
										+ e, e);
			}

			@Override
			public boolean wasScheduledForCancel() {
				return false;
			}

			@Override
			public void setProgress(int progress) {
				// Can be ignored
			}

		});
	}

	public void execute(final IInternalTaskCompanion<R> companion) {
		final Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				R re = null;
				try {
					re = doInBackground(companion);
				} catch (Exception e) {
					log.debug("An exception occured during task execution. "
							+ e, e);
					throw new ExecutionException(e);
				}
				log.debug("Execution was finished with returnvalue '" + re
						+ "'");
				companion.finished(re);
			}
		});
		thread
				.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

					@Override
					public void uncaughtException(Thread t, Throwable e) {
						log.debug(
								"An uncaught exception was caught during task execution. "
										+ e, e);
						if (e instanceof Exception) {
							if (companion != null) {
								companion.setException((Exception) e);
								companion.finished(null);
							} else {
								log
										.error("The workers companion may never be null");
							}
						} else {
							log.error(
									"An Error occured during task execution. "
											+ e.getMessage(), e);
							exit(e);
						}
					}

				});
		thread.start();
	}

	private void exit(Throwable e) {
		JOptionPane.showMessageDialog(null, "An fatal error occured.\n"
				+ "For details refer to the application log.\n"
				+ "The program must be cosed immeditely.\n" + e, "Fatal Error",
				JOptionPane.ERROR_MESSAGE);
		log.error("Cannot handle Error. " + e, e);
		System.exit(1);
	}

}
